﻿// Copyright (c) 2014 Morten Bakkedal
// This code is published under the MIT License.

using System;
using System.Collections.Generic;

using FuncLib;
using FuncLib.Functions;
using FuncLib.Mathematics;

namespace FuncLibSamples.Testing.ComplexFunctions
{
	[Serializable]
	public abstract partial class ComplexFunction
	{
		private Dictionary<ComplexVariable, ComplexFunction> derivatives;

		public Complex Value(IEvaluator evaluator)
		{
			return ComputeValue(evaluator);
		}

		protected abstract Complex ComputeValue(IEvaluator evaluator);

		public ComplexFunction Derivative(ComplexVariable variable)
		{
			if (derivatives == null)
			{
				derivatives = new Dictionary<ComplexVariable, ComplexFunction>();
			}

			ComplexFunction derivative;
			if (!derivatives.TryGetValue(variable, out derivative))
			{
				derivative = ComputeDerivative(variable);
				derivatives.Add(variable, derivative);
			}

			return derivative;
		}

		protected abstract ComplexFunction ComputeDerivative(ComplexVariable variable);

		protected virtual Function Re()
		{
			// Use this helper class by default.
			return new ReFunction(this);
		}

		protected virtual Function Im()
		{
			// Use this helper class by default.
			throw new NotImplementedException();
		}

		public static Function Re(ComplexFunction f)
		{
			return f.Re();
		}

		public static Function Im(ComplexFunction f)
		{
			throw new NotImplementedException();
		}

		[Serializable]
		private class ReFunction : Function
		{
			private ComplexFunction f;

			public ReFunction(ComplexFunction f)
			{
				this.f = f;
			}

			protected override double ComputeValue(IEvaluator evaluator)
			{
				Complex z;
				//if (!evaluator.TryGetValue<Function, Complex>(this, out z))
				//{
					z = f.ComputeValue(evaluator);
				//	evaluator.Add<Function, Complex>(this, z);
				//}

				return Complex.Re(z);
			}

			protected override Function ComputeDerivative(Variable variable)
			{
				if (!(variable is ComplexInnerVariable)) // no - see ComplexRectangularFunction
				{
					// A ComplexFunction can only depend on ComplexInnerVariable.
					return 0.0;
				}

				return 2.0 * ComplexFunction.Re(f.Derivative(((ComplexInnerVariable)variable).ComplexVariable));
			}
		}
	}
}
